/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.actionbarsherlock.internal.view.menu;

import java.util.ArrayList;
import java.util.List;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.view.MenuItem;
import android.util.SparseBooleanArray;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;

import com.actionbarsherlock.sample.demos.R;

/**
 * MenuPresenter for building action menus as seen in the action bar and action
 * modes.
 */
public class ActionMenuPresenter extends BaseMenuPresenter {
	// UNUSED private static final String TAG = "ActionMenuPresenter";

	private int mWidthLimit;
	private int mActionItemWidthLimit;
	private int mMaxItems;
	private boolean mMaxItemsSet;
	private boolean mStrictWidthLimit;
	private boolean mWidthLimitSet;

	private int mMinCellSize;

	private AlertDialog mDialog;

	// Group IDs that have been added as actions - used temporarily, allocated
	// here for reuse.
	private final SparseBooleanArray mActionButtonGroups = new SparseBooleanArray();

	private View mScrapActionButtonView;
	int mOpenSubMenuId;

	public ActionMenuPresenter(Context context) {
		super(context, R.layout.abs__action_menu_layout, R.layout.abs__action_menu_item_layout);
	}

	@Override
	public void initForMenu(Context context, MenuBuilder menu) {
		super.initForMenu(context, menu);

		final Resources res = context.getResources();

		if (!mWidthLimitSet) {
			mWidthLimit = res.getDisplayMetrics().widthPixels / 2;
		}

		// Measure for initial configuration
		if (!mMaxItemsSet) {
			mMaxItems = res.getInteger(R.integer.abs__max_action_buttons);
		}

		mActionItemWidthLimit = mWidthLimit;

		mMinCellSize = (int) (ActionMenuView.MIN_CELL_SIZE * res.getDisplayMetrics().density);

		// Drop a scrap view as it may no longer reflect the proper
		// context/config.
		mScrapActionButtonView = null;
	}

	public void onConfigurationChanged(Configuration newConfig) {
		if (!mMaxItemsSet) {
			mMaxItems = mContext.getResources().getInteger(R.integer.abs__max_action_buttons);
			if (mMenu != null) {
				mMenu.onItemsChanged(true);
			}
		}
	}

	public void setWidthLimit(int width, boolean strict) {
		mWidthLimit = width;
		mStrictWidthLimit = strict;
		mWidthLimitSet = true;
	}

	public void setItemLimit(int itemCount) {
		mMaxItems = itemCount;
		mMaxItemsSet = true;
	}

	@Override
	public MenuView getMenuView(ViewGroup root) {
		MenuView result = super.getMenuView(root);
		((ActionMenuView) result).setPresenter(this);
		return result;
	}

	@Override
	public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) {
		View actionView = item.getActionView();
		if (actionView == null) {
			if (!(convertView instanceof ActionMenuItemView)) {
				convertView = null;
			}
			actionView = super.getItemView(item, convertView, parent);
		}
		actionView.setVisibility(View.VISIBLE);

		final ActionMenuView menuParent = (ActionMenuView) parent;
		final ViewGroup.LayoutParams lp = actionView.getLayoutParams();
		if (!menuParent.checkLayoutParams(lp)) {
			actionView.setLayoutParams(menuParent.generateLayoutParams(lp));
		}
		return actionView;
	}

	@Override
	public void bindItemView(MenuItemImpl item, MenuView.ItemView itemView) {
		itemView.initialize(item, 0);

		final ActionMenuView menuView = (ActionMenuView) mMenuView;
		ActionMenuItemView actionItemView = (ActionMenuItemView) itemView;
		actionItemView.setItemInvoker(menuView);
	}

	@Override
	public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) {
		return item.isActionButton();
	}

	@Override
	public boolean filterLeftoverView(ViewGroup parent, int childIndex) {
		return super.filterLeftoverView(parent, childIndex);
	}

	public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
		if (!subMenu.hasVisibleItems())
			return false;

		SubMenuBuilder topSubMenu = subMenu;
		while (topSubMenu.getParentMenu() != mMenu) {
			topSubMenu = (SubMenuBuilder) topSubMenu.getParentMenu();
		}
		View anchor = findViewForItem(topSubMenu.getItem());
		if (anchor == null) {
			return false;
		}

		mOpenSubMenuId = subMenu.getItem().getItemId();

		final List<MenuItemImpl> items = subMenu.getVisibleItems();
		final int itemsSize = items.size();
		final CharSequence[] itemText = new CharSequence[itemsSize];
		for (int i = 0; i < itemsSize; i++) {
			itemText[i] = items.get(i).getTitle();
		}

		AlertDialog.Builder builder = new AlertDialog.Builder(mContext).setTitle(subMenu.getItem().getTitle()).setIcon(subMenu.getItem().getIcon())
				.setCancelable(true).setOnCancelListener(new DialogInterface.OnCancelListener() {
					@Override
					public void onCancel(DialogInterface dialog) {
						mDialog = null;
					}
				});

		final boolean isExclusive = ((MenuItemImpl) subMenu.getItem(0)).isExclusiveCheckable();
		final boolean isCheckable = ((MenuItemImpl) subMenu.getItem(0)).isCheckable();
		if (isExclusive) {
			int selected = -1;
			for (int i = 0; i < itemsSize; i++) {
				if (items.get(i).isChecked()) {
					selected = i;
					break;
				}
			}
			builder.setSingleChoiceItems(itemText, selected, new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {
					items.get(which).invoke();
					dialog.dismiss();
					mDialog = null;
				}
			});
		} else if (isCheckable) {
			boolean[] selected = new boolean[itemsSize];
			for (int i = 0; i < itemsSize; i++) {
				selected[i] = items.get(i).isChecked();
			}
			builder.setMultiChoiceItems(itemText, selected, new DialogInterface.OnMultiChoiceClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which, boolean isChecked) {
					items.get(which).setChecked(isChecked);
					dialog.dismiss();
					mDialog = null;
				}
			});
		} else {
			builder.setItems(itemText, new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {
					items.get(which).invoke();
					dialog.dismiss();
					mDialog = null;
				}
			});
		}

		mDialog = builder.show();
		super.onSubMenuSelected(subMenu);
		return true;
	}

	private View findViewForItem(MenuItem item) {
		final ViewGroup parent = (ViewGroup) mMenuView;
		if (parent == null)
			return null;

		final int count = parent.getChildCount();
		for (int i = 0; i < count; i++) {
			final View child = parent.getChildAt(i);
			if (child instanceof MenuView.ItemView && ((MenuView.ItemView) child).getItemData() == item) {
				return child;
			}
		}
		return null;
	}

	/**
	 * Dismiss all popup menus - overflow and submenus.
	 * 
	 * @return true if popups were dismissed, false otherwise. (This can be
	 *         because none were open.)
	 */
	public boolean dismissPopupMenus() {
		return hideSubMenus();
	}

	/**
	 * Dismiss all submenu popups.
	 * 
	 * @return true if popups were dismissed, false otherwise. (This can be
	 *         because none were open.)
	 */
	public boolean hideSubMenus() {
		if (mDialog != null) {
			try {
				mDialog.dismiss();
			} catch (Exception e) {
				// Must have been dismissed or cancelled already
			}
			mDialog = null;
			return true;
		}
		return false;
	}

	public boolean flagActionItems() {
		final ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems();
		final int itemsSize = visibleItems.size();
		int maxActions = mMaxItems;
		int widthLimit = mActionItemWidthLimit;
		final int querySpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
		final ViewGroup parent = (ViewGroup) mMenuView;

		int requiredItems = 0;
		// int requestedItems = 0;
		int firstActionWidth = 0;
		for (int i = 0; i < itemsSize; i++) {
			MenuItemImpl item = visibleItems.get(i);
			if (item.requiresActionButton()) {
				requiredItems++;
			}/*
			 * else if (item.requestsActionButton()) { requestedItems++; }
			 */
		}
		maxActions -= requiredItems;

		final SparseBooleanArray seenGroups = mActionButtonGroups;
		seenGroups.clear();

		int cellSize = 0;
		int cellsRemaining = 0;
		if (mStrictWidthLimit) {
			cellsRemaining = widthLimit / mMinCellSize;
			final int cellSizeRemaining = widthLimit % mMinCellSize;
			cellSize = mMinCellSize + cellSizeRemaining / cellsRemaining;
		}

		// Flag as many more requested items as will fit.
		for (int i = 0; i < itemsSize; i++) {
			MenuItemImpl item = visibleItems.get(i);

			if (item.requiresActionButton()) {
				View v = getItemView(item, mScrapActionButtonView, parent);
				if (mScrapActionButtonView == null) {
					mScrapActionButtonView = v;
				}
				if (mStrictWidthLimit) {
					cellsRemaining -= ActionMenuView.measureChildForCells(v, cellSize, cellsRemaining, querySpec, 0);
				} else {
					v.measure(querySpec, querySpec);
				}
				final int measuredWidth = v.getMeasuredWidth();
				widthLimit -= measuredWidth;
				if (firstActionWidth == 0) {
					firstActionWidth = measuredWidth;
				}
				final int groupId = item.getGroupId();
				if (groupId != 0) {
					seenGroups.put(groupId, true);
				}
				item.setIsActionButton(true);
			} else if (item.requestsActionButton()) {
				// Items in a group with other items that already have an action
				// slot
				// can break the max actions rule, but not the width limit.
				final int groupId = item.getGroupId();
				final boolean inGroup = seenGroups.get(groupId);
				boolean isAction = (maxActions > 0 || inGroup) && widthLimit > 0 && (!mStrictWidthLimit || cellsRemaining > 0);

				if (isAction) {
					View v = getItemView(item, mScrapActionButtonView, parent);
					if (mScrapActionButtonView == null) {
						mScrapActionButtonView = v;
					}
					if (mStrictWidthLimit) {
						final int cells = ActionMenuView.measureChildForCells(v, cellSize, cellsRemaining, querySpec, 0);
						cellsRemaining -= cells;
						if (cells == 0) {
							isAction = false;
						}
					} else {
						v.measure(querySpec, querySpec);
					}
					final int measuredWidth = v.getMeasuredWidth();
					widthLimit -= measuredWidth;
					if (firstActionWidth == 0) {
						firstActionWidth = measuredWidth;
					}

					if (mStrictWidthLimit) {
						isAction &= widthLimit >= 0;
					} else {
						// Did this push the entire first item past the limit?
						isAction &= widthLimit + firstActionWidth > 0;
					}
				}

				if (isAction && groupId != 0) {
					seenGroups.put(groupId, true);
				} else if (inGroup) {
					// We broke the width limit. Demote the whole group, they
					// all overflow now.
					seenGroups.put(groupId, false);
					for (int j = 0; j < i; j++) {
						MenuItemImpl areYouMyGroupie = visibleItems.get(j);
						if (areYouMyGroupie.getGroupId() == groupId) {
							// Give back the action slot
							if (areYouMyGroupie.isActionButton())
								maxActions++;
							areYouMyGroupie.setIsActionButton(false);
						}
					}
				}

				if (isAction)
					maxActions--;

				item.setIsActionButton(isAction);
			}
		}
		return true;
	}

	@Override
	public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
		dismissPopupMenus();
		super.onCloseMenu(menu, allMenusAreClosing);
	}

	@Override
	public Parcelable onSaveInstanceState() {
		SavedState state = new SavedState();
		state.openSubMenuId = mOpenSubMenuId;
		return state;
	}

	@Override
	public void onRestoreInstanceState(Parcelable state) {
		SavedState saved = (SavedState) state;
		if (saved.openSubMenuId > 0) {
			MenuItem item = mMenu.findItem(saved.openSubMenuId);
			if (item != null) {
				SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
				onSubMenuSelected(subMenu);
			}
		}
	}

	private static class SavedState implements Parcelable {
		public int openSubMenuId;

		SavedState() {
		}

		SavedState(Parcel in) {
			openSubMenuId = in.readInt();
		}

		@Override
		public int describeContents() {
			return 0;
		}

		@Override
		public void writeToParcel(Parcel dest, int flags) {
			dest.writeInt(openSubMenuId);
		}

		@SuppressWarnings("unused")
		public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
			public SavedState createFromParcel(Parcel in) {
				return new SavedState(in);
			}

			public SavedState[] newArray(int size) {
				return new SavedState[size];
			}
		};
	}
}
